﻿

#region Using Statements

// System References
using System;
using System.Text;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.IO;
using System.Threading;

// XNA References
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

// Internal References
using Freeform.Framework.Graphics;
using Freeform.Framework.Utility;
using Freeform.Framework.Input;
using Freeform.Framework.Geometry;
using Freeform.Framework.Geometry.Collision;
using Game = Freeform.Framework.Game;

#endregion


namespace Freeform.Framework
{
    /// <summary>
    /// The world class maintains the state of a world that can be rendered to a screen via a viewport.
    /// </summary>
    public class World : Element
    {

        #region Members

        /// <summary>
        /// Gets a list of all the entities that are contained within this world.
        /// </summary>
        public ReadOnlyCollection<Entity> Entities { get { return _EntList.AsReadOnly(); } }
        private List<Entity> _EntList { get; set; }
        private Dictionary<UInt64, Entity> _EntLib { get; set; }


        /// <summary>
        /// Gets the viewports that belong to this world.
        /// </summary>
        public ReadOnlyCollection<Viewport2D> Viewports { get; private set; }
        internal List<Viewport2D> _Viewports { get; private set; }


        /// <summary>
        /// Gets or sets the background colour of the world.
        /// </summary>
        public Color BackgroundColour { get; set; }


        /// <summary>
        /// Gets the process properties for the collision detection routine.
        /// </summary>
        public ProcessProperties CollisionDetection { get; private set; }


        /// <summary>
        /// Determines the method of handling mutual collisions.
        /// </summary>
        public CollisionChecks CollisionChecks { get; set; }

        #endregion


        #region Init

        /// <summary>
        /// Creates a new 2d world.
        /// </summary>
        public World() 
        {
            _EntLib = new Dictionary<UInt64, Entity>();
            _EntList = new List<Entity>();
            _Viewports = new List<Viewport2D>();
            Viewports = _Viewports.AsReadOnly();
            CollisionDetection = new ProcessProperties();
            CollisionDetection.ProcessHandling = ProcessHandling.Sequential;
            CollisionChecks = CollisionChecks.Mutual;
            Game.RegisterWorld(this);
        }

        #endregion


        #region Update

        /// <summary>
        /// Collision detection is performed in the last part of the Update function after all movements have been resolved.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        protected override void EndUpdate(GameTime Time)
        {
            base.EndUpdate(Time);

            // Collision detection.
            if (CollisionDetection.ProcessHandling == ProcessHandling.Sequential)
                CheckCollisions();

            #if WINDOWS
            // Multithreaded collision detection.
            else if (CollisionDetection.ProcessHandling == ProcessHandling.Multithread)
            {
                if (CollisionDetection.Thread == null || CollisionDetection.Thread.ThreadState != ThreadState.Running)
                {
                    CollisionDetection.Thread = new Thread(CheckCollisions);
                    CollisionDetection.Thread.Start();
                }
            }
            #endif
        }


        /// <summary>
        /// Allows the world to update itself and all sub components.
        /// </summary>
        /// <param name="Time">Provides a snapshot of timing values.</param>
        protected override void DoUpdate(GameTime Time)
        {
            // Update entity list.
            _EntList = _EntLib.Values.ToList();

            // Update the viewports that observe this world.
            for (Int32 i = 0; i < Viewports.Count; i++)
                if (Viewports[i].IsEnabled && !Viewports[i].IsDisposed)
                    Viewports[i].Update(Time);

            // Update the entities in the world.
            foreach (Entity Ent in Entities)
                if (Ent.IsEnabled && !Ent.IsDisposed)
                    Ent.Update(Time);

            base.DoUpdate(Time);
        }

        #endregion


        #region Dispose

        /// <summary>
        /// Disposes this world and removes it from the game.
        /// </summary>
        protected override void OnDispose()
        {
            base.OnDispose();

            // Dispose all viewports.
            while (Viewports.Count > 0)
                Viewports[0].Dispose();

            // Dispose all entities in this world.
            foreach (Entity Ent in Entities)
                Ent.Dispose();

            // Remove this world from the game.
            Game.UnregisterWorld(this);
        }

        #endregion


        #region Utility

        /// <summary>
        /// Iterates through each entity checking if it has collided with another entity.
        /// </summary>
        public virtual void CheckCollisions()
        {
            /*
            for (Int32 i = 0; i < ComponentList.ActiveColliders.Count; i++)
            {
                // If mutual collisions are on, we only check from i + 1.
                Int32 j = 0;
                if (CollisionChecks != CollisionChecks.TwoWay)
                    j = i + 1;

                // Check collisions against other active entities.
                for (; j < ComponentList.ActiveColliders.Count; j++)
                    if (i != j)
                        if (ComponentList.ActiveColliders[i].CollisionProperties.CheckCollision(
                            ComponentList.ActiveColliders[j].CollisionProperties))
                            ComponentList.ActiveColliders[i].CollisionProperties.FireCollision(ComponentList.ActiveColliders[j], CollisionChecks == CollisionChecks.Mutual);

                // Reset j for passive collision entities.
                j = 0;

                // Check collisions against other passive entities.
                for (; j < ComponentList.PassiveColliders.Count; j++)
                    if (ComponentList.ActiveColliders[i].CollisionProperties.CheckCollision(
                        ComponentList.PassiveColliders[j].CollisionProperties))
                        ComponentList.ActiveColliders[i].CollisionProperties.FireCollision(ComponentList.PassiveColliders[j], CollisionChecks == CollisionChecks.Mutual);
            }*/
        }


        /// <summary>
        /// Registers the Entity and adds it to the World's list of Entities.
        /// </summary>
        /// <param name="Ent">The Entity to register.</param>
        public virtual void RegisterEntity(Entity Ent) { _EntLib.Add(Ent.ID, Ent); }


        /// <summary>
        /// Unregisters the Entity from the World's list of Entities.
        /// </summary>
        /// <param name="Ent">The Entity to unregister.</param>
        public virtual void UnregisterEntity(Entity Ent) { _EntLib.Remove(Ent.ID); }


        /// <summary>
        /// Adds the viewport to the game.
        /// </summary>
        /// <param name="Viewport">The world to add.</param>
        public void RegisterViewport(Viewport2D Viewport) { _Viewports.Add(Viewport); }


        /// <summary>
        /// Removes the viewport from the game.
        /// </summary>
        /// <param name="Viewport">The world to remove.</param>
        public void UnregisterViewport(Viewport2D Viewport) { _Viewports.Remove(Viewport); }

        #endregion

    }
}